﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using UnityEngine;
using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain;

namespace LCL
{
    class Modulable : IDisposable
    {
        public virtual void Dispose()
        {
            throw new NotImplementedException();
        }
        public virtual void Initialize()
        {
        }

        public virtual void Uninitialize()
        {

        }
    }

    class HotfixModule : Modulable
    {
        private static HotfixModule _instance = null;

        public HotfixModule()
            //: base(ClassKey.GetType<HotfixModule>())
        {
            //单例
            _instance = this;
        }

        public override void Dispose()
        {
            //单例
            _instance = null;

            base.Dispose();
        }

        public override void Initialize()
        {
            base.Initialize();

        }

        public override void Uninitialize()
        {
            base.Uninitialize();
        }

        private byte[] _pdbBytes;
        private byte[] _dllBytes;
        public static void TestSetDllPdbBytes(byte[] dll, byte[] pdb)
        {
            if (_instance != null)
            {
                _instance._pdbBytes = pdb;
                _instance._dllBytes = dll;
            }
        }

        public static void Start()
        {
            _instance?.StartImpl();
        }

        public static void Stop()
        {
            _instance?.StopImpl();
        }

        //AppDomain是ILRuntime的入口，最好是在一个单例类中保存，整个游戏全局就一个，这里为了示例方便，每个例子里面都单独做了一个
        //大家在正式项目中请全局只创建一个AppDomain
        ILRuntime.Runtime.Enviorment.AppDomain appdomain;

        private void StartImpl()
        {
            var dll = this.GetDllBytes();
            if (dll == null)
            {
                Debug.LogError("加载 EA4A55FD.dll 失败");
                return;
            }

            // 首先实例化ILRuntime的AppDomain，AppDomain是一个应用程序域，每个AppDomain都是一个独立的沙盒
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();

            var dllms = new System.IO.MemoryStream(dll);

            // PDB文件是调试数据库，如需要在日志中显示报错的行号，则必须提供PDB文件，
            // 不过由于会额外耗用内存，正式发布时请将PDB去掉，下面LoadAssembly的时候pdb传null即可
            var pdb = this.GetPdbBytes();
            System.IO.MemoryStream pdbms = null;
            Mono.Cecil.Pdb.PdbReaderProvider pdbReader = null;
            if (pdb != null)
            {
                pdbms = new System.IO.MemoryStream(pdb);
                pdbReader = new Mono.Cecil.Pdb.PdbReaderProvider();
            }

            // 创建程序集
            appdomain.LoadAssembly(dllms, pdbms, pdbReader);
            dllms.Dispose();
            pdbms?.Dispose(); 
            
            this.InitializeILRuntime();
            this.InitializeHotFix();
            TestBugFunc();
        }

        private void StopImpl()
        {
            if (appdomain != null)
            {
                this.UninitializeHotFix();
                appdomain = null;
            }
        }

        private byte[] GetDllBytes()
        {
            return this._dllBytes;
            //return this.GetFileBytes("EA4A55FD.dll.bytes");
        }

        private byte[] GetPdbBytes()
        {
            return this._pdbBytes;
            //return this.GetFileBytes("EA4A55FD.dll.pdb.bytes");
        }

        //private byte[] GetFileBytes(string file)
        //{
        //    byte[] dll = www.bytes;

        //}

        private void InitializeILRuntime()
        {
            //这里做一些ILRuntime的注册，HelloWorld示例暂时没有需要注册的

            appdomain.AllowUnboundCLRMethod = true;

            this.DoLCLFunctionDelegateReg();
            //LCLFunctionDelegate.Reg(appdomain);

            //AdapterRegister.RegisterCrossBindingAdaptor(m_Assembly);
        }

        private void DoLCLFunctionDelegateReg()
        {
            //Type type = typeof(LCLFunctionDelegate);
            
            // 查找指定类
            Type type = Assembly.GetExecutingAssembly().GetType("LCLFunctionDelegate");
            if (type == null)
            {
                Debug.LogError("无法找到 LCLFunctionDelegate 类。");
                return;
            }

            // 查找类上面的指定函数
            var flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
            var args = new Type[] { typeof(ILRuntime.Runtime.Enviorment.AppDomain) };
            var method = type.GetMethod("Reg", flag, null, args, null);
            if (method != null && method.IsStatic)
            {
                //LCLFunctionDelegate.Reg(appdomain);
                method.Invoke(null, new []{ this.appdomain });
            }
            else
            {
                Debug.LogError("无法找到 LCLFunctionDelegate.Reg 函数。");
            }
        }

        private void InitializeHotFix()
        {
            // 初始化hotfix.dll
            appdomain?.Invoke("HotFix_Project.HotFixMain", "Initialize", null, null);
        }

        private void UninitializeHotFix()
        {
            // 反初始化hotfix.dll
            appdomain?.Invoke("HotFix_Project.HotFixMain", "Uninitialize", null, null);
        }

        private void TestBugFunc()
        {
            for (int i = 0; i < 10; i++)
            {

            }
            Debug.LogError("我是一个BUG！");
        }

    }
}
